package com.tree.leetcode.linklist;

import com.tree.leetcode.common.ListNode;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import static com.tree.leetcode.common.ListNode.*;

/**
 * package com.tree.leecode.linklist
 * description: 简单
 * 请判断一个链表是否为回文链表。
 * <p>
 * 示例 1:
 * 输入: 1->2
 * 输出: false
 * <p>
 * 示例 2:
 * 输入: 1->2->2->1
 * 输出: true
 * <p>
 * 进阶：
 * 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题？
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/palindrome-linked-list
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 *
 * @author tree
 * @date 2020-03-05 09:04
 */
public class PalindromeLinkList_234 {

    public static void main(String[] args) {
        ListNode node = nodeBuilder();
        printNodeResult(node);

//        System.out.println("isPalindrome = "+isPalindromeWithStack(node));
//        System.out.println("isPalindrome = "+isPalindromeWithHalfArray(node));
//        System.out.println("isPalindrome = "+isPalindromeWithArray(node));

        System.out.println("isPalindrome = " + isPalindromeWithDoublePointer(node));

    }

    /**
     * 快慢指针
     * 1. 找到中间节点，
     * 2. 从中间节点反转链表
     * 3. 判断是否回文
     *
     * @param head
     * @return
     */
    private static boolean isPalindromeWithDoublePointer(ListNode head) {
        // 1. 找到中间节点
        ListNode fast = head;
        ListNode slow = fast;

        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;

        }
        System.out.print("打印slow：");
        printNodeResult(slow);
        System.out.print("打印head：");
        printNodeResult(head);

        //2. 从中间节点反转链表
        ListNode cur = slow.next;
        while (cur != null) {
            ListNode next = cur.next;
            cur.next = slow;
            slow = cur;
            cur = next;
        }
        System.out.print("打印slow：");
        printNodeResult(slow);
        System.out.print("打印head：");
        printNodeResult(head);

        // 3. 判断是否回文
        //head == slow 奇数
        //head.next == slow 偶数
//        while (head != slow || head.next != slow) {
//            if (head.val != slow.val) {
//                return false;
//            }
//            head = head.next;
//            slow = slow.next;
//        }

        while (head != slow) {
            if (head.val != slow.val) {
                return false;
            }
            // 判断偶数节点回文
            if (head.next == slow) {
                return true;
            }
            head = head.next;
            slow = slow.next;
        }

        //走到这代表已经是奇数或者偶数节点的最后一个节点了
        //head == slow 奇数
        //head.next == slow 偶数
        return true;
    }


    private static boolean isPalindromeWithArray(ListNode head) {
        ListNode current = head;
        ArrayList<Integer> list = new ArrayList<>();
        while (current != null) {
            list.add(current.val);
            current = current.next;
        }

        int start = 0;
        int end = list.size() - 1;
        while (start <= end) {
            if (!list.get(start).equals(list.get(end))) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }

    private static boolean isPalindromeWithHalfArray(ListNode head) {
        ListNode current = head;
        int size = 0;
        while (current != null) {
            size++;
            current = current.next;
        }

        //是否是奇数
        boolean isEvenNum = isEvenNum(size);
        //后半段开始位置
        int begin = isEvenNum ? size / 2 + 2 : size / 2 + 1;
        int end = size;
        ArrayList<Integer> list = buildList(head, begin, end);

        return compareList(head, list);
    }

    private static ArrayList<Integer> buildList(ListNode head, int begin, int end) {
        ArrayList<Integer> list = new ArrayList<>();

        ListNode current = head;
        int size = 1;
        while (current != null) {
            if (size >= begin && size <= end) {
                list.add(current.val);
            }
            size++;
            current = current.next;
        }
        return list;
    }


    /**
     * 是否是回文
     * 后半段用栈
     *
     * @param head
     * @return
     */
    public static boolean isPalindromeWithStack(ListNode head) {
        int size = 1;
        ListNode listNode = head;
        while (listNode != null && listNode.next != null) {
            listNode = listNode.next;
            size++;
        }

        //如果只有1个节点
        if (listNode == null || size == 1) {
            System.out.println("没有节点或者只有1个节点， 是回文");
            return true;
        }

        //是否是奇数
        boolean isEvenNum = isEvenNum(size);
        //后半段开始位置
        int begin2 = isEvenNum ? size / 2 + 2 : size / 2 + 1;
        //后半段结束位置
        int end2 = size;
        System.out.println("size = " + size + ", 是奇数 = " + isEvenNum);
        System.out.println("前半段开始位置=1， 前半段结束位置=" + (size / 2) + ", 后半段开始begin2=" + begin2 + ", 后半段结束位置end2=" + end2);
        Stack<Integer> stack = buildStack(head, begin2, end2);
        return compare(head, stack);
    }

    /**
     * 放入后半段的值到stack中，
     *
     * @param head
     * @param begin2
     * @param end2
     * @return
     */
    private static Stack<Integer> buildStack(ListNode head, int begin2, int end2) {
        ListNode curNode = head;
        int size = 1;
        Stack<Integer> stack = new Stack();

        while (curNode != null) {
            if (size >= begin2 && size <= end2) {
                stack.push(curNode.val);
            }
            size++;
            curNode = curNode.next;
        }
        return stack;
    }

    private static boolean compareList(ListNode head, List<Integer> list) {
        ListNode curNode = head;
        int size = list.size();

        for (int i = size - 1; i >= 0; i--) {
            int value = curNode.val;
            int value2 = list.get(i);
            if (value != value2) {
                return false;
            }
            curNode = curNode.next;
        }

        return true;
    }

    private static boolean compare(ListNode head, Stack<Integer> stack) {
        ListNode curNode = head;
        int size = stack.size();
        for (int i = 0; i < size; i++) {
            int value = curNode.val;
            int value2 = stack.pop();
            if (value != value2) {
                return false;
            }
            curNode = curNode.next;
        }
        return true;
    }

    public static boolean isEvenNum(int size) {
        return size % 2 == 0 ? false : true;
    }
}
